package groovy.lang.webobjects;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.foundation._NSUtilities;

public class WOComponentCloner {

	public static WOComponent clone(final WOComponent src) {
		if (src == null)
			return null;

		WOContext aContext = src.context();
		WOComponent aParent = src.parent();

		WOComponent dst;
		try {			
			Class<?> dstClass = _NSUtilities.classWithName(src.getClass().getName());
			dst = (WOComponent) _NSUtilities.instantiateObject(dstClass,
					new Class[] { WOContext.class }, 
					new Object[] { src.context() }, true, false);
			copyFields(src, dst);
			if (aParent != null)
				aParent._setSubcomponent(dst, aContext.elementID());
			return dst;
		} catch (Throwable e) {
			System.out.println("Unable to clone " + src.getClass().getName());
			return src;
		}
	}

	private static void copyFields(Object fromInstance, Object toInstance) throws Throwable {
		if (fromInstance == null || toInstance == null) {
			return;
		}
		Class<?> clazzFrom = fromInstance.getClass();
		for (Class<?> c = clazzFrom; c != Object.class; c = c.getSuperclass ()) {
			_setFields(fromInstance, toInstance, c.getDeclaredFields());
		}
	}

	private static void _setFields(final Object src, final Object dest, final Field[] fields) 
			throws Throwable {
		for (int f = 0, fieldsLength = fields.length; f < fieldsLength; ++ f) {
			final Field field = fields[f];
			
			try {
				Class<?> destClass = Class.forName(field.getDeclaringClass().getName(), false, 
						dest.getClass().getClassLoader());
				Field destField = destClass.getDeclaredField(field.getName());
				
				final int modifiers = destField.getModifiers ();
	            
	            if ((Modifier.STATIC & modifiers) != 0) continue;
	            if ((Modifier.FINAL & modifiers) != 0) continue;
				
				field.setAccessible(true);
				destField.setAccessible(true);
				Object value = field.get(src);
				
				if (value == null)
				    destField.set(dest, null);
				else {
					destField.set(dest, value);						
				}
			} catch (NoSuchFieldException e) {
				continue;
			} catch (Throwable e) {
				throw e;
			}
		}
	}
}
